package net.xuele.learn.lock;

import org.apache.commons.lang3.StringUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @Author: yubo
 * @date: 9:41 2020/4/15
 * @Description: 类描述
 */

public class TestCache {

    public static void main(String[] args) {
        FakeCacheV2 fakeCache = new FakeCacheV2();

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10, 100, 0, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(100), new ThreadFactoryImpl("cache-pool-"), new ThreadPoolExecutor.AbortPolicy());
        for (int i = 0; i < 10; i++) {
            threadPoolExecutor.submit(() -> {

                String hello = fakeCache.get("hello");
                System.out.println(Thread.currentThread().getName() + " has got the value " + hello);

            });
        }

    }


    static class FakeCache {
        private Map<String, String> cache = new HashMap<>();

        private AtomicBoolean invoked = new AtomicBoolean(false);

        private CountDownLatch latch = new CountDownLatch(1);

        private String getFromDB(String key) {
            System.out.println(Thread.currentThread().getName() + " has enter and try to get the value");
            if (invoked.compareAndSet(false, true)) {
                System.out.println(Thread.currentThread().getName() + " has enter and compute the value");
                String value = computeValue();
                cache.put(key, value);
                latch.countDown();
                invoked.set(false);
                return value;
            } else {
                try {
                    latch.await();
                    Thread.sleep(5000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return cache.get(key);
            }
        }

        public String get(String key) {
            String value = cache.get(key);
            return StringUtils.isNotBlank(value) ? value : getFromDB(key);
        }

        private String computeValue() {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return UUID.randomUUID().toString().replaceAll("-", "");
        }
    }


    static class FakeCacheV2 {

        // 在缓存过期的时间点之前将state字段的字段值换成INITIAL

        private AtomicInteger state = new AtomicInteger(INITIAL);

        public static final int INITIAL = 0;

        public static final int INVOKED = 1;

        public static final int FINISHED = 2;

        private Map<String, String> cache = new HashMap<>();

        private String getFromDB(String key) {
            System.out.println(Thread.currentThread().getName() + " has enter and try to get the value");
            if (state.compareAndSet(INITIAL, INVOKED)) {
                System.out.println(Thread.currentThread().getName() + " has enter and compute the value");
                if (StringUtils.isNotBlank(cache.get(key))) {
                    return cache.get(key);
                }
                String value = computeValue();
                cache.put(key, value);
                state.compareAndSet(INVOKED, FINISHED);
                return value;
            } else {
                for (; ; ) {
                    if (state.get() == FINISHED) {
                        System.out.println(Thread.currentThread().getName()+" is trying to get the value");
                        return cache.get(key);
                    }
                }

            }
        }

        public String get(String key) {
            String value = cache.get(key);
            return StringUtils.isNotBlank(value) ? value : getFromDB(key);
        }

        private String computeValue() {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return UUID.randomUUID().toString().replaceAll("-", "");
        }
    }

}
